/*-------------------------------------------------------------------------*/
/**
  @file		md_io.c
  @author	Renaud Wanschoor
  @date	july 2004
  @version	$Revision: 2.23 $
  @brief	basic Input/Output functions

  implement command line parsing, log recording, stat computing for
  log files
  /todo grid and graph reading are not implemented yet, it would be 
  a nice idea to implement general graph and grid reading functions
  that could be used by either pagrid, metis, party,... it would reduce 
  duplication of those functions
  
*/
/*--------------------------------------------------------------------------*/
#include <plib.h>

#define PARSE_ARG -2
/**********************************************************************
 **********************************************************************
 *   IO_args
 **********************************************************************
 **********************************************************************/
static int parse__(int, char *av[], char*);
static void help__(char *,int*,int*,int*,char*);

int P_idx = 1;
char* P_arg = NULL;

/** 
 * \brief read and store arguments and options passed to the command line 
 *
 * input:
 *   - argc : argc got by main (number of arguments)
 *   - argv : argv got by main (array of string of arguments)
 *   - def  : default options
 *   - odef : parameter to be used when the small cap option is used on the command line
 *   - a    : authorized parameters
 *   - opt  : list of possible options (string of options)
 *   - c    : ctrl structure (where parameters are stored 
 */ 
void IO_args(int argc, char *argv[],int *def, int *odef, int *a, char *opt, IoCtrl *c){
  char *arg;
  int o;
  char serfile[256];
  char filename1[256],filename2[256];
  FILE* fp;
  int args = 0,i;
  int d[IO_DEF],od[IO_DEF],aut[IO_DEF];
  char valo[256];
  int oldidx,newidx;

  /* preparation */
  strcpy(serfile,IO_FSER);
  if(def == IO_DEFAULT){
    d[IO_DWEIGHT]	= IO_WEIGHT_NORM;
    d[IO_DMATCH] 	= IO_MATCH_SHEM;
    d[IO_DUNCO]		= IO_UNCO_KL;
    d[IO_DOUT] 		= IO_OUT_NOLEVEL;
    d[IO_DSER] 		= IO_SER_NONE;
    d[IO_DRAND] 	= IO_RAND_NONE;
    d[IO_DBGLVL] 	= 0;
    d[IO_DPRE]		= IO_PRE_BOUND;
    d[IO_DIM]		= 30;
    d[IO_DTOL]		= 30;
    d[IO_DFLOW]		= 20;
    d[IO_DBAL]		= 10000;
  }else{
    memcpy(d,def,IO_DEF*sizeof(int));
  }
  if(def == IO_DEFAULT){
    od[IO_DWEIGHT]	= IO_WEIGHT_UPGMA;
    od[IO_DMATCH] 	= IO_MATCH_MWM;
    od[IO_DUNCO]	= IO_UNCO_KLEXEC;
    od[IO_DOUT]		= IO_OUT_LEVEL;
    od[IO_DRAND] 	= IO_RAND_ALL;
    od[IO_DBGLVL] 	= 255;
    od[IO_DPRE]		= IO_PRE_BOUND1;
  }else{
    memcpy(od,odef,IO_DEF*sizeof(int));
  }
  if(a == IO_DEFAULT){
    aut[IO_DMATCH] 	= 775;
    aut[IO_DWEIGHT]	= 3;
    aut[IO_DUNCO]	= 7;
    aut[IO_DOUT]	= 7;
    aut[IO_DSER] 	= 7;
    aut[IO_DRAND] 	= 15;
    aut[IO_DPRE]	= 7;
  }else{
    memcpy(aut,a,IO_DEF*sizeof(int));
  }
  if(opt == IO_DEFAULT)
    strcpy(valo,"wWmMsSdDrRoO");
  else
    strcpy(valo,opt);

  /* init */
  c->WType 		= d[IO_DWEIGHT];
  c->CType 		= d[IO_DMATCH];
  c->RType		= d[IO_DUNCO];
  c->out[IO_RESULT]	= d[IO_DOUT];
  c->out[IO_SER] 	= d[IO_DSER];
  c->PSeed		= d[IO_DRAND];
  c->dbglvl		= d[IO_DBGLVL];
  c->preference		= d[IO_DPRE];
  c->im			= d[IO_DIM];
  c->tol		= d[IO_DTOL];
  c->flow		= d[IO_DFLOW];
  c->bal		= d[IO_DBAL];
  
  c->out[IO_ID] = c->debug = c->CSeed = c->RSeed = c->ISeed = 0;
  c->et         = 0;
  c->quad       = 0;

  c->NBytes     = 10;

  c->F_graph = c->F_grid = NULL;
  strcpy(c->F_output,"");
  strcpy(c->F_serialize,"");
  
  /* lets help dummy users */

  if(argc == 1){
    help__(argv[0],d,od,aut,valo);
    exit(0);
  }

  /* read line arguments 
   * forbidden options until now (second letter):
   * used options : 
   * c d o p q r s w e
   * C B D F I O P R S T U W 
   */
  oldidx = P_idx;
  while((o = parse__(argc,argv,valo)) != EOF){
    newidx = P_idx;
    /* convert to lower case */
    if(o != PARSE_ARG){
      arg = P_arg;
      if(arg != NULL){
        while((int)*arg != (int)'\0'){
  	  *arg = tolower(*arg);
	  arg++;
        }
      }
    }
    switch(o){
    case 'B':
      c->bal = atoi(P_arg);
      break;
    case 'c':
      c->CType = od[IO_DMATCH];
      break;
    case 'C':
      if((IO_MATCH_RM & aut[IO_DMATCH]) && strcmp(P_arg,"rm") == 0)
	c->CType = IO_MATCH_RM;
      else if((IO_MATCH_MWM & aut[IO_DMATCH]) && strcmp(P_arg,"mwm") == 0)
	c->CType = IO_MATCH_MWM;
      else if((IO_MATCH_HEM & aut[IO_DMATCH]) && strcmp(P_arg,"hem") == 0)
	c->CType = IO_MATCH_HEM;
      else if((IO_MATCH_SHEM & aut[IO_DMATCH]) && strcmp(P_arg,"shem") == 0)
	c->CType = IO_MATCH_SHEM;
      else if((IO_MATCH_LAM & aut[IO_DMATCH]) && strcmp(P_arg,"lam") == 0)
	c->CType = IO_MATCH_LAM;
      else
	ERROR((1,"-C option error unrecognized value %s\n",P_arg));
      break;
    case 'd':
      c->dbglvl = od[IO_DBGLVL];
      break;
    case 'D':
      c->dbglvl = atoi(P_arg);
      break;
    case 'e':
      c->et = 1;
      break;
    case 'F':
      c->flow = atoi(P_arg);
      break;
    case 'G':
      c->NBytes = atoi(P_arg);
      break;
    case 'I':
      c->im = atoi(P_arg);
      break;
    case 'o':
      c->out[IO_RESULT] = od[IO_DOUT];
      break;
    case 'O':
      if((IO_OUT_NO & aut[IO_DOUT]) && strcmp(P_arg,"no") == 0)
	c->out[IO_RESULT] = IO_OUT_NO;
      else if((IO_OUT_LEVEL & aut[IO_DOUT]) && strcmp(P_arg,"level") == 0)
	c->out[IO_RESULT] = IO_OUT_LEVEL;
      else if((IO_OUT_NOLEVEL & aut[IO_DOUT]) && strcmp(P_arg,"nolevel") == 0)
	c->out[IO_RESULT] = IO_OUT_NOLEVEL;
      else
	ERROR((1,"-O option error unrecognized value %s\n",P_arg));
      break;
    case 'p':
      c->preference = od[IO_DPRE];
      break;
    case 'P':
      if((IO_PRE_FULL & aut[IO_DPRE]) && strcmp(P_arg,"full") == 0)
	c->preference = IO_PRE_FULL;
      else if((IO_PRE_BOUND & aut[IO_DPRE]) && strcmp(P_arg,"boundary") == 0)
	c->preference = IO_PRE_BOUND;
      else if((IO_PRE_BOUND1 & aut[IO_DPRE]) && strcmp(P_arg,"boundary+1") == 0)
	c->preference = IO_PRE_BOUND1;
      else
	ERROR((1,"-P option error unrecognized value %s\n",P_arg));
      break;
    case 'q':
      c->quad = 1;
      break;
    case 'r':
      c->PSeed = od[IO_DRAND];
      break;
    case 'R':
      if((IO_RAND_NONE & aut[IO_DRAND]) && strcmp(P_arg,"none") == 0)
	c->PSeed = IO_RAND_NONE;
      else if((IO_RAND_REFINE & aut[IO_DRAND]) && strcmp(P_arg,"refine") == 0)
	c->PSeed = IO_RAND_REFINE;
      else if((IO_RAND_INI & aut[IO_DRAND]) && strcmp(P_arg,"ini") == 0)  
	c->PSeed = IO_RAND_INI;
      else if((IO_RAND_ALL & aut[IO_DRAND]) && strcmp(P_arg,"all") == 0)  
	c->PSeed = IO_RAND_ALL;
      else
	ERROR((1,"-R option error unrecognized value %s\n",P_arg));
      break;
    case 's':
      if((IO_SER_DO & aut[IO_DSER])){
	c->out[IO_SER] = IO_SER_DO;
	strcpy(c->F_serialize, serfile);
      }
      break;
    case 'S':
      if(IO_SER_USE & aut[IO_DSER]){
	/* test if file exists */
	ERROR(((fp = fopen(P_arg,"r"))==NULL || fclose(fp) == EOF,
	       "-ser option error file %s not found\n",P_arg));
	c->out[IO_SER] = IO_SER_USE;
	strcpy(c->F_serialize, P_arg);
      }
      break;
    case 'T':
      c->tol = atoi(P_arg);
      break;
    case 'u':
      c->RType = od[IO_DUNCO];
      break;
    case 'U':
      if((IO_UNCO_GREEDY & aut[IO_DUNCO]) && strcmp(P_arg,"greedy") == 0)
	c->RType = IO_UNCO_GREEDY;
      else if((IO_UNCO_KL & aut[IO_DUNCO]) && strcmp(P_arg,"kl") == 0)
	c->RType = IO_UNCO_KL;
      else if((IO_UNCO_KLEXEC & aut[IO_DUNCO]) && strcmp(P_arg,"exec") == 0)
	 c->RType = IO_UNCO_KLEXEC;
      else
	ERROR((1,"-U option error unrecognized value %s\n",P_arg));
      break;
    case 'w':
      c->WType = od[IO_DWEIGHT];
      break;
    case 'W':
      if((IO_WEIGHT_NORM & aut[IO_DWEIGHT]) && strcmp(P_arg,"norm") == 0)
	c->WType = IO_WEIGHT_NORM;
      else if((IO_WEIGHT_UPGMA & aut[IO_DWEIGHT]) && strcmp(P_arg,"upgma") == 0)
	c->WType = IO_WEIGHT_UPGMA;
      else
	ERROR((1,"-W option error unrecognized value %s\n",P_arg));
      break;
    case '-' :
      help__(argv[0],d,od,aut,valo);
      exit(0);
    case PARSE_ARG:
      switch(args++){
      case 0:
	c->F_graph = P_arg;
	ERROR(((fp = fopen(P_arg,"r")) == NULL || fclose(fp) == EOF,
	       "cannot find graph file %s\n",P_arg));
	break;
      case 1:
	c->F_grid = P_arg;
	ERROR(((fp = fopen(P_arg,"r")) == NULL || fclose(fp) == EOF,
	       "cannot find grid file %s\n",P_arg));
	break;
      default:
	NOTICE((1,"too many arguments passed %s %s %s\n",
	       c->F_graph,c->F_grid,P_arg));
      }
      break;
    default:
      ERROR((1,"Error while parsing arguments passed to %s\n",argv[0]));
    }
    oldidx = newidx;
  }

  ERROR((args != 2,"no enough arguments passed\n"));

  /* options are settled know some business link to options */
  /* random seed */
  switch(c->PSeed){
  case IO_RAND_NONE:
    c->PSeed = c->CSeed = c->ISeed = c->RSeed = IO_SEED;
    break;
  case IO_RAND_REFINE:
#ifndef WINDOWS
    srand((unsigned)time(NULL) + (unsigned)getpid());
#else
    srand((unsigned)time(NULL));
#endif
    c->PSeed = c->CSeed = c->ISeed = IO_SEED;
    c->RSeed = (int)rand();
    break;
  case IO_RAND_INI:
#ifndef WINDOWS
    srand((unsigned)time(NULL) + (unsigned)getpid());
#else
    srand((unsigned)time(NULL));
#endif
    c->PSeed = c->CSeed = IO_SEED;
    c->ISeed = (int)rand();
    c->RSeed = (int)rand();
    break;
  case IO_RAND_ALL:
#ifndef WINDOWS
    srand((unsigned)time(NULL) + (unsigned)getpid());
#else
    srand((unsigned)time(NULL));
#endif
    c->PSeed = (int)rand();
    c->CSeed = (int)rand();
    c->ISeed = (int)rand();
    c->RSeed = (int)rand();
    break;
  }

  /* output filename */
  U_filename(filename1,c->F_graph);
  U_filename(filename2,c->F_grid);
  switch(c->out[IO_RESULT]){
  case IO_OUT_LEVEL:
    /* find file id */
    for (i=1;;i++){
      sprintf(c->F_output,"%s.%s.result.%d",filename1,filename2,i);
      if ((fp=fopen(c->F_output,"r")) == NULL || fclose(fp) == EOF){
	c->out[IO_ID] = i;
	break;
      }
    }
    ERROR(((fp=fopen(c->F_output,"w")) == NULL || fclose(fp) == EOF,"cannot write %s\n",c->F_output));    
  case IO_OUT_NOLEVEL:
  case IO_OUT_NO:
    sprintf(c->F_output,"%s.%s",filename1,filename2);
    break;
  }
}

/**
 * \brief parsing function for the command line
 *
 * parse the command line, must called until the whole command line is
 * parsed. It updates two global variable: P_idx the position in argv and P_arg
 * the argument that come with an option (if there is any). The command line
 * is parsed with unix style : -A ALL means option a with argument ALL -abc means 
 * there are 3 options, a, b and c. Arguments can only be passed to upper case options
 * 
 * input : 
 *  - ac  : argc of main
 *  - av  : argv of main
 *  - def : possible options
 * 
 * output:
 *  - the readen option or PARSE_ARG if there was no option
 */
int parse__(int ac, char *av[], char* def){
  char* a=av[P_idx];
  char op;
  P_arg = NULL;

  if(P_idx < 1 || P_idx >= ac){
    P_idx = ac;
    return EOF;
  }
  /* not an option */
  if((int) *a != (int) '-'){
    P_arg = a;
    P_idx++;
    return PARSE_ARG;
  }
  /* help */
  if((int) *(a+1) == (int) '-'){
    P_idx++;
    return *(a+1);
  }

  a++;
  (av[P_idx])++;

  /* search into def */
  while((int)*a != (int)*def && (int)*def != (int)'\0')
    def++;

  /* option not in def */
  WARNING(((int)*def == (int)'\0',"%s error : invalid option -%s",av[0],a),EOF);

  op = *a;

  /* upper case waiting argument */
  if(isupper((int)*def)){
    /* no space */
    if((int)*(a+1) != '\0'){
      P_arg = a+1;
      return op;
    /* no arg */
    }else if(P_idx >= ac - 1){
      P_idx = ac;
      return EOF;
    /* good boy */
    }else{
      P_arg = av[P_idx + 1];
      P_idx += 2;
      return op;
    }
  }
  /* not upper case */
  /* if there is another option */
  if((int)*(a+1) != (int)'\0'){
    *a = '-';
    return op;
  }

  /* stupid alone option */
  P_idx++;
  return op;
}

/** help function to type the correct command line */
void help__(char *prog,int *def, int* odef, int *autval, char* autopt){
  int c = (strchr(autopt,'c') != NULL);
  int d = (strchr(autopt,'d') != NULL);
  int e = (strchr(autopt,'e') != NULL);
  int o = (strchr(autopt,'o') != NULL);
  int p = (strchr(autopt,'p') != NULL);
  int q = (strchr(autopt,'q') != NULL);
  int s = (strchr(autopt,'s') != NULL);
  int r = (strchr(autopt,'r') != NULL);
  int u = (strchr(autopt,'u') != NULL);
  int w = (strchr(autopt,'w') != NULL);

  int B = (strchr(autopt,'B') != NULL);
  int C = (strchr(autopt,'C') != NULL);
  int D = (strchr(autopt,'D') != NULL);
  int F = (strchr(autopt,'F') != NULL);
  int I = (strchr(autopt,'I') != NULL);
  int O = (strchr(autopt,'O') != NULL);
  int P = (strchr(autopt,'P') != NULL);
  int R = (strchr(autopt,'R') != NULL);
  int S = (strchr(autopt,'S') != NULL);
  int T = (strchr(autopt,'T') != NULL);
  int U = (strchr(autopt,'U') != NULL);
  int W = (strchr(autopt,'W') != NULL);
  int G = (strchr(autopt,'G') != NULL);
  
  printf("%s\n\n",prog);
  printf("usage : %s [-",prog);
  for(;(int)*autopt != (int)'\0';autopt++){
    if(islower((int)*autopt))
      putchar(*autopt);
  }
  printf("]");
  if(B)
    printf(" [-B swap]");
  if(C)
    printf(" [-C coarsening]");
  if(D)
    printf(" [-D level]");
  if(F)
    printf(" [-F iterations]");
  if(I)
    printf(" [-I imbalance]");
  if(O)
    printf(" [-O output]");
  if(P)
    printf(" [-P preference]");
  if(R)
    printf(" [-R random]");
  if(S)
    printf(" [-S file]");
  if(T)
    printf(" [-T swap]");
  if(U)
    printf(" [-U uncoarsening]");
  if(W)
    printf(" [-W weight]");
  if(G)
    printf(" [-G num_bytes_send_per_v]");
  printf("\n<graph file> <grid file>\n\n");
  printf("default values (without the corresponding option) might be changed by the tool)\n");

  if(B){
    printf("  ETBal swap   : %d\n",def[IO_DBAL]);
  }
  if(c || C){
    printf("  coarsening   : ");
    switch(def[IO_DMATCH]){
    case IO_MATCH_RM:
      printf("using random matching for coarsening\n");
      break;
    case IO_MATCH_HEM:
      printf("using heavy edge matching for coarsening\n");
      break;
    case IO_MATCH_SHEM:
      printf("using modify heavy edge matching for coarsening\n");
      break;
    case IO_MATCH_SHEMKWAY:
      printf("using SHEMKWAY matching for coarsening\n");
      break;
    case IO_MATCH_SHEBM_ONENORM:
      printf("using SHEBM ONENORM matching for coarsening\n");
      break;
    case IO_MATCH_SHEBM_INFNORM:
      printf("using SHEBM INFNORM weighted matching for coarsening\n");
      break;
    case IO_MATCH_SBHEM_ONENORM:
      printf("using SBHEM ONENORM matching for coarsening\n");
      break;
    case IO_MATCH_SBHEM_INFNORM:
      printf("using SBHEM INFNORM matching for coarsening\n");
      break;
    case IO_MATCH_MWM:
      printf("using maximum weighted matching for coarsening\n");
      break;
    case IO_MATCH_LAM:
      printf("using lam (from party) for coarsening\n");
      break;
    }
  }
  if(d || D)
    printf("  level        : %d\n",def[IO_DBGLVL]);
  if(e)
    printf("  exe load bal : no execution time load balancing\n");
  if(F)
    printf("  iterations   : %d\n",def[IO_DFLOW]);
  if(I)
    printf("  imbalance    : %d %%\n",def[IO_DIM]);
  if(o || O){
    printf("  output       : ");
    switch(def[IO_DOUT]){
    case IO_OUT_NO:
      printf("no result out put\n");
      break;
    case IO_OUT_LEVEL:
      printf("allow multiple run of the partitionner (results in different files\n");
      break;
    case IO_OUT_NOLEVEL:
      printf("all results in the same file\n");
      break;
    }
  }
  if(p || P){
    printf("  preference   : ");
    switch(def[IO_DPRE]){
    case IO_PRE_FULL:
      printf("entire vertices preference\n");
      break;
    case IO_PRE_BOUND:
      printf("boundary vertices preference\n");
      break;
    case IO_PRE_BOUND1:
      printf("boudnary+1 vertices preference\n");
      break;
    }
  }
  if(r || R){
    printf("  random       : ");
    switch(def[IO_DRAND]){
    case IO_RAND_NONE:
      printf("all seeds fixed\n");
      break;
    case IO_RAND_REFINE:
      printf("coarsening and initial partitioning seeds fixed\n");
      break;
    case IO_RAND_ALL:
      printf("random seeds\n");
      break;
    case IO_RAND_INI:
      printf("coarsening seed fixed\n");
    }
  }
  if(s || S){
    printf("  file         : ");
    switch(def[IO_DSER]){
    case IO_SER_NONE:
      printf("serializing not used\n");
      break;
    case IO_SER_DO:
      printf("serialize coarsening into matching.%s",IO_FSER);
      break;
    }
  }
  if(T){
    printf("  swap         : %d\n",def[IO_DTOL]);
  }
  if(u || U){
    printf("  uncoarsening : ");
    switch(def[IO_DUNCO]){
    case IO_UNCO_GREEDY:
      printf("greedy refinement\n");
      break;
    case IO_UNCO_KL:
      printf("Kerighan lin refinement\n");
      break;
    case IO_UNCO_KLEXEC:
      printf("execution time Kerighan lin refinement\n");
      break;
    } 
  }
  if(w || W){
    printf("  weight       : ");
    switch(def[IO_DWEIGHT]){
    case IO_WEIGHT_NORM:
      printf("standard\n");
      break;
    case IO_WEIGHT_UPGMA:
      printf("UPGMA\n");
      break;
    } 
  }
  putchar('\n');
  /* lower case default value for option */
  if(c){
    printf("  c : ");
    switch(odef[IO_DMATCH]){
    case IO_MATCH_RM:
      printf("using random matching for coarsening\n");
      break;
    case IO_MATCH_HEM:
      printf("using heavy edge matching for coarsening\n");
      break;
    case IO_MATCH_SHEM:
      printf("using modify heavy edge matching for coarsening\n");
      break;
    case IO_MATCH_SHEMKWAY:
      printf("using SHEMKWAY matching for coarsening\n");
      break;
    case IO_MATCH_SHEBM_ONENORM:
      printf("using SHEBM ONENORM matching for coarsening\n");
      break;
    case IO_MATCH_SHEBM_INFNORM:
      printf("using SHEBM INFNORM weighted matching for coarsening\n");
      break;
    case IO_MATCH_SBHEM_ONENORM:
      printf("using SBHEM ONENORM matching for coarsening\n");
      break;
    case IO_MATCH_SBHEM_INFNORM:
      printf("using SBHEM INFNORM matching for coarsening\n");
      break;
    case IO_MATCH_MWM:
      printf("using maximum weighted matching for coarsening\n");
      break;
    case IO_MATCH_LAM:
      printf("using lam (from party) for coarsening\n");
      break;
    }
  }
  if(d){
    printf("  d : debug level set to %d\n",odef[IO_DBGLVL]);
  }
  if(e){
    printf("  e : execution time load balancing\n");
  }
  if(o){
    printf("  o : ");
    switch(odef[IO_DOUT]){
    case IO_OUT_NO:
      printf("no result output (only stats and serialized matching)\n");
      break;
    case IO_OUT_LEVEL:
      printf("put multiple run into different files (matching.nb.%s and\n",IO_FSER);
      printf("      <graph file>.<network cost matrix file>.result.nb\n");
      break;
    case IO_OUT_NOLEVEL:
      printf("put multiple run into same file\n");
      break;
    }
  }
  if(p){
    printf("  p : preference, ");
    switch(odef[IO_DPRE]){
    case IO_PRE_FULL:
      printf("entire vertices preference\n");
      break;
    case IO_PRE_BOUND:
      printf("boundary vertices preference\n");
      break;
    case IO_PRE_BOUND1:
      printf("boudnary+1 vertices preference\n");
      break;
    }
  }
  if(q){
    printf("  q : use quadratic path length in the cost function\n");
  }
  if(r){
    printf("  r : random settings, ");
    switch(odef[IO_DRAND]){
    case IO_RAND_NONE:
      printf("use fix seeds\n");
      break;
    case IO_RAND_REFINE:
      printf("use random seed for refinement");
      break;
    case IO_RAND_ALL:
      printf("use random seeds\n");
      break;
    case IO_RAND_INI:
      printf("use random seed for refinement and initial partitioning\n");
      break;
    }
  }
  if(s){
    printf("  s : serialize matching into matching.%s\n",IO_FSER);
  }
  if(u){
    printf("  u : ");
    switch(odef[IO_DUNCO]){
    case IO_UNCO_GREEDY:
      printf("using greedy algorithm for refinement\n");
      break;
    case IO_UNCO_KL:
      printf("using kerighan lin algorithm for refinement\n");
      break;
    case IO_UNCO_KLEXEC:
      printf("using execution time kl algorithm for refinement\n");
      break;
    }
  }
  if(w){
    printf("  w : ");
    switch(odef[IO_DWEIGHT]){
    case IO_WEIGHT_NORM:
      printf("using standard edge weight for matching\n");
      break;
    case IO_WEIGHT_UPGMA:
      printf("using UPGMA edge weight for matching\n");
      break;
    }
  }
  putchar('\n');
  /* upper case passing value to option */
  if(B){
    printf("  B swap         : largest allowed size of recent move list in execution time load\n");
    printf("                   balancing when the partition is unbalanced, trying to achieve\n");
    printf("                   balanced partition in terms of estimated execution time\n");
  }
  if(C){
    printf("  C coarsening   : matching scheme\n");
    if(IO_MATCH_RM & autval[IO_DMATCH])
      printf("                     - RM (Random Matching)\n");
    if(IO_MATCH_MWM & autval[IO_DMATCH])
      printf("                     - MWM (Maximum Weighted Matching)\n");
    if(IO_MATCH_HEM & autval[IO_DMATCH])
      printf("                     - HEM (Heavy Edge Matching)\n");
    if(IO_MATCH_SHEM & autval[IO_DMATCH])
      printf("                     - SHEM (modified Heavy Edge Matching)\n");
    if(IO_MATCH_LAM & autval[IO_DMATCH])
      printf("                     - LAM (lam algorithm from party)\n");
  }
  if(D){
    printf("  D level        : debug level (integer)\n");
  }
  if(F){
    printf("  F iterations   : maximum times of re-calculating balancing flow in vertex weight load\n");
    printf("                   balancing of each refinement stage.  (Note that two adjacent vertices\n");
    printf("                   can become non-adjacent when one of them migration to balance the partition\n");
  }
  if(I){
    printf("  I imbalance    : imbalance (interger percent)\n");
  }
  if(O){
    printf("  O output       : select ouput options\n");
    if(IO_OUT_NO & autval[IO_DOUT])
      printf("                     - NO (no result output only stats)\n");
    if(IO_OUT_NOLEVEL & autval[IO_DOUT])
      printf("                     - NOLEVEL (result always written into the same file)\n");
    if(IO_OUT_LEVEL & autval[IO_DOUT])
      printf("                     - LEVEL (results written into different files)\n");
  }
  if(P){
    printf("  P preference   : vertices preference\n");
    if(IO_PRE_FULL & autval[IO_DPRE])
      printf("                     - FULL (entire vertices preference)\n");
    if(IO_PRE_BOUND & autval[IO_DPRE])
      printf("                     - BOUNDARY (boundary vertices preference)\n");
    if(IO_PRE_BOUND1 & autval[IO_DPRE])
      printf("                     - BOUNDARY+1 (boudnary+1 vertices preference)\n");
  }
  if(R){
    printf("  R random       : can fix seeds random\n");
    if(IO_RAND_NONE & autval[IO_DRAND])
      printf("                     - NONE (fixed seeds)\n");
    if(IO_RAND_REFINE & autval[IO_DRAND])
      printf("                     - REFINE (coarsening and initial partitioning seeds fixed)\n");
    if(IO_RAND_ALL & autval[IO_DRAND])
      printf("                     - ALL (using random seeds)\n");
    if(IO_RAND_INI & autval[IO_DRAND])
      printf("                     - INI (coarsening seed fixed)\n");
  }
  if(S){
    printf("  S file         : use the binary serialized matching file\n");
  }
  if(T){
    printf("  T swap         : integer size of recent move list when the partition is balanced\n");
  }
  if(U){
    printf("  U uncoarsening : select refinement algorithm\n");
    if(IO_UNCO_GREEDY & autval[IO_DUNCO])
      printf("                     - GREEDY (greedy algo)\n");
    if(IO_UNCO_KL & autval[IO_DUNCO])
      printf("                     - KL (kerighan lin)\n");
    if(IO_UNCO_KLEXEC & autval[IO_DUNCO])
      printf("                     - EXEC (kerighan lin execution time)\n");
  }
  if(W){
    printf("  W weight       : select matching edge weight\n");
    if(IO_WEIGHT_NORM & autval[IO_DWEIGHT])
      printf("                     - NORM (standard weight)\n");
    if(IO_WEIGHT_UPGMA & autval[IO_DWEIGHT])
      printf("                     - UPGMA (Unweighted Pair Group Method using Arithmetic average\n");
  }
  if(G){
    printf("  G num_bytes_send_per_v  :  define number of bytes data that a vertex will send to others, default is 10.\n");
  }
  printf("  --help       : this screen\n");
}

/**********************************************************************
 **********************************************************************
 *   IO_writeLogs
 **********************************************************************
 **********************************************************************/
/**
 * \brief general partition recorder for METIS and PAGRID, ...
 *
 * input :
 *   - tool  : library to use (TOOL_PAGRID,...)
 *   - graph : graph structure
 *   - file  : string naming file to write
 */
#ifndef PLOT
void IO_write(int tool,void* graph,void* grid,char* F_output,int level,int id){
  PlotGraphType* pgraph;
  int* where = NULL;
  int i,nvtxs=0;
  FILE* fp;
  char output[256];

  CALL2VAL(tool,TransGraph(graph),pgraph);

  if(level == IO_OUT_NO)
    return;
  
  nvtxs = pgraph->nvtxs;
  where = pgraph->pro;

  if(level == IO_OUT_LEVEL)
    sprintf(output,"%s.result.%d",F_output,id);
  else
    sprintf(output,"%s.result",F_output);

  ERROR(((fp = fopen(output,"w")) == NULL,
	"IO_write error code 1 : cannot open result file %s for writing\n",output));

  for(i=0;i<nvtxs;i++)
    fprintf(fp,"%d\n",where[i]);

  PLIBG_Free(pgraph);

  fclose(fp);
}
#endif

#define R_025  0
#define R_05   1
#define R_1    2
#define R_2    3

/**
 * \brief compute execution time stat about a partition
 * 
 * compute general info about a partition, the volume of communication is
 * estimated more precisely
 * 
 * input:
 *   - tool  : partitionner (TOOL_PAGRID,...)
 *   - graph : application graph
 *   - grid  : computational grid
 *
 * output:
 *   - v_met   : maximum execution time
 *   - v_proet : execution time of processors (must be freed)
 */ 
int IO_statVol(int tool,void* graph,void* grid,double *v_met,double** v_proET){
  PlotGraphType* pgraph;
  PlotGridType*  pgrid;

  double met       = 0;

  int    i,j,jstart,jend;
  int    v,w,prov,prow;
  int    nparts,**netcost=NULL,*penalty=NULL,nvtxs = 0;
  double **ratio = NULL;
  double *prospeed = NULL;
  int    *where = NULL,*vwgt;
  int **connect = NULL;
  int **latency1 = NULL;
  double* proportion;

  int    *procs;
  double *uproet;
  double ref=0.0;

  int *adjidx,*adjncy,*adjwgt;

#ifndef PLOT
  CALL2VAL(tool,TransGraph(graph),pgraph);
  CALL2VAL(tool,TransGrid(grid),pgrid);
#else
  pgraph = graph;
  pgrid  = grid;
#endif

  ref    = PLIBN_RatioRef(pgrid,0);

  nparts     = pgrid->npro;
  netcost    = pgrid->netcost;
  penalty    = pgrid->penalty;
  proportion = pgrid->proportion;
  prospeed   = pgrid->prospeed;
  ratio      = pgrid->ratio;
  connect    = pgrid->connect;
  latency1   = pgrid->latency1;
  
  nvtxs  = pgraph->nvtxs;
  vwgt   = pgraph->vwgt;
  where  = pgraph->pro;
  adjidx = pgraph->adjidx;
  adjncy = pgraph->adjncy;
  adjwgt = pgraph->adjwgt;
  
  procs  = U_mallocset(nparts,int,0,"IO_statvol error code 3\n");
  uproet = U_mallocset(nparts,double,0,"IO_statvol error code 4\n");  

  /* computiiiiiiiiing */
  for (v=0; v<nvtxs; v++){
    prov = where[v];

    for(j=0;j<nparts;j++){
      procs[j] = 0;
    } 

    jstart = adjidx[v];
    jend   = adjidx[v+1];
   
    for (j = jstart; j < jend; j++){
      w    = adjncy[j];
      prow = where[w];
      
      if(prov != prow && adjwgt[j] > procs[prow]){
	procs[prow] = adjwgt[j];	
      }
    }

    for(j=0;j<nparts;j++){
      if(procs[j] == 0)
	continue;
      uproet[prov] += procs[j] * ratio[prov][j];      
    }
    
    uproet[prov]       += vwgt[v] * prospeed[prov];
  }
//  for(i = 0; i < nparts; i++){
//    for(j = 0; j < nparts; j++){
//printf("connect[%d][%d] \n", i, j);
//      if(connect[i][j] > 0){
//        uproet[i] += latency1[i][j];
//      }
//  }
//}

  for (i=0; i<nparts; i++){
    if(uproet[i]>met) met = uproet[i];
  }

  /* pass results */
  *v_met       = met;
  *v_proET     = uproet;
  
  U_free("IO_stat error code 5",&procs,LEND);

#ifndef PLOT
  PLIBG_Free(pgraph);
  PLIBN_Free(pgrid);  
#endif

  return nparts;
}

/**
 * \brief compute statistics about a partition
 * 
 * compute general info about a partition, the volume of communication is
 * overestimated
 * 
 * input:
 *   - tool  : partitionner (TOOL_PAGRID,...)
 *   - graph : application graph
 *   - grid  : computational grid
 *
 * output:
 *   - v_cutedge   : cut edge weight
 *   - v_tcc       : total communication cost
 *   - v_imbalance : vertex weight imbalance
 *   - v_met       : maximum execution time
 *   - v_met025    : maximum execution time replacing the user ratio with 0.25
 *   - v_met05     : maximum execution time replacing the user ratio with 0.5
 *   - v_met1      : maximum execution time replacing the user ratio with 1
 *   - v_met2      : maximum execution time replacing the user ratio with 2
 *   - v_met       : maximum execution time
 *   - v_proVW     : sum of vertex weights assigned to each processor (must be freed)
 *   - v_proET     : execution time of processors (must be freed)
 */ 
int IO_stat(int tool,void* graph,void *grid,
	    double *v_cutedge,double *v_tcc,
	    double *v_imbalance,
	    double *v_met,double *v_met025,
	    double *v_met05,double *v_met1,double *v_met2,
	    int **v_proVW,double **v_proET){
  PlotGraphType* pgraph;
  PlotGridType*  pgrid;

  int    cutedge   = 0;
  int    tcc       = 0;
  double met       = 0;
  double met025    = 0;
  double met05     = 0;
  double met1      = 0;
  double met2      = 0;

  int    i,j,jstart,jend;
  int    v,w,prov,prow;
  int    sumprowt,maxprowt;
  double comtime;
  int    nparts,**netcost=NULL,*penalty=NULL,nvtxs = 0;
  double **ratio = NULL;
  double *prospeed = NULL;
  int    *where = NULL,*vwgt;
  double* proportion;

  int    *prowt;
  double *uproet,**proet;
  double ref=0.0,ref025=0.0,ref05=0.0,ref1=0.0,ref2=0.0;

  int *adjidx,*adjncy,*adjwgt;

#ifndef PLOT
  CALL2VAL(tool,TransGraph(graph),pgraph);
  CALL2VAL(tool,TransGrid(grid),pgrid);
#else
  pgraph = graph;
  pgrid  = grid;
#endif

  ref    = PLIBN_RatioRef(pgrid,0);
  ref025 = PLIBN_RatioRef(pgrid,0.25);
  ref05  = PLIBN_RatioRef(pgrid,0.5);
  ref1   = PLIBN_RatioRef(pgrid,1);
  ref2   = PLIBN_RatioRef(pgrid,2);
  
  nparts     = pgrid->npro;
  netcost    = pgrid->netcost;
  penalty    = pgrid->penalty;
  proportion = pgrid->proportion;
  prospeed   = pgrid->prospeed;
  ratio      = pgrid->ratio;
  
  nvtxs  = pgraph->nvtxs;
  vwgt   = pgraph->vwgt;
  where  = pgraph->pro;
  adjidx = pgraph->adjidx;
  adjncy = pgraph->adjncy;
  adjwgt = pgraph->adjwgt;
  
  prowt  = U_mallocset(nparts,int,0,"IO_stat error code 3\n");
  uproet = U_mallocset(nparts,double,0,"IO_stat error code 4\n");
  proet  = U_mallocxy(4,nparts,double,"IO_stat error code 5\n");

  for(i=0;i<nparts;i++){
    proet[R_025][i] = 0;
    proet[R_05][i]  = 0;
    proet[R_1][i]   = 0;
    proet[R_2][i]   = 0;
  }

  /* computiiiiiiiiing */
  for (v=0; v<nvtxs; v++){
    prov = where[v];

    jstart = adjidx[v];
    jend   = adjidx[v+1];
   
    for (j = jstart; j < jend; j++){
      w    = adjncy[j];
      prow = where[w];
      
      if (prov != prow){
	comtime = adjwgt[j] * ratio[prov][prow];
	uproet[prov]       += comtime;
	proet[R_025][prov] += comtime * ref025 / ref;
	proet[R_05][prov]  += comtime * ref05 / ref;
	proet[R_1][prov]   += comtime * ref1 / ref;
	proet[R_2][prov]   += comtime * ref2 / ref;
	
	cutedge += adjwgt[j];
	tcc     += adjwgt[j] * netcost[prov][prow];
      }
    }
    
    prowt[prov]        += vwgt[v];
    uproet[prov]       += vwgt[v] * prospeed[prov];
    proet[R_025][prov] += vwgt[v] * prospeed[prov];
    proet[R_05][prov]  += vwgt[v] * prospeed[prov];
    proet[R_1][prov]   += vwgt[v] * prospeed[prov];
    proet[R_2][prov]   += vwgt[v] * prospeed[prov];
  }
  
  /* imbalance */
  sumprowt = 0;
  maxprowt = 0;
  for (i=0; i<nparts; i++){
    sumprowt += (double)(prowt[i] + penalty[i]);
    if(prowt[i] + penalty[i]>maxprowt) maxprowt = prowt[i] + penalty[i];
    if(uproet[i]>met) met = uproet[i];
    if(proet[R_025][i]>met025) met025 = proet[R_025][i];
    if(proet[R_05][i]>met05) met05 = proet[R_05][i];
    if(proet[R_1][i]>met1) met1 = proet[R_1][i];
    if(proet[R_2][i]>met2) met2 = proet[R_2][i];
  }
  
  /* pass results */
  *v_cutedge   = cutedge/2;
  *v_tcc       = tcc/2;
  *v_imbalance = (double)(nparts * maxprowt)/(double)sumprowt;
  *v_met       = met;
  *v_met025    = met025;
  *v_met05     = met05;
  *v_met1      = met1;
  *v_met2      = met2;
  *v_proVW     = prowt;
  *v_proET     = uproet;
  
  U_free("IO_stat error code 5",&proet,LEND);

#ifndef PLOT
  PLIBG_Free(pgraph);
  PLIBN_Free(pgrid);  
#endif

  return nparts;
}

/**
 * \brief general log recorder for METIS and PAGRID, ...
 *
 * input :
 *   - tool  : library to use (TOOL_PAGRID,...)
 *   - graph : graph structure
 *   - file  : string naming file to write
 */
void IO_writeLog(int tool,void *graph, void *grid, int edgecut,
		 int ctype, int wtype, int rtype,
		 int cseed, int iseed, int rseed,
		 char* F_graph, char* F_grid, char* F_output,char *F_serialize,
		 int out_id,
		 float t_coa, float t_ini, float t_ref, float t_execbal, float t_tot){
  char filename[256];
  int i;
  FILE* fp;
  int nparts = 0;
  
  char *method = NULL;
  
  double v_cutedge,v_tcc,v_imbalance,v_met,v_met025,v_met05,v_met1,v_met2;
  double et_sum = 0, et_max = 0;
  int *v_proVW;
  double *v_proET;

  nparts = IO_stat(tool,graph,grid,&v_cutedge,&v_tcc,
		   &v_imbalance,&v_met,&v_met025,
		   &v_met05,&v_met1,&v_met2,
		   &v_proVW,&v_proET);

  CALL2VAL(tool,GetScheme(ctype,wtype,rtype,cseed,iseed,rseed),method);

  /* .vwt.log */
  strcpy(filename,F_output);
  strcat(filename, ".log");
  if ((fp=fopen(filename,"a"))==NULL){
    printf("WriteLog error code 6 : cannot generate output time log file %s\n",filename);
    exit(0);
  }
  
  if (ftell(fp) == 0){
    fprintf(fp,"#%s\n",F_graph);
    fprintf(fp,"#%s\n",F_grid);
    fprintf(fp,"# Final result stats\n");
    for (i=0; i<nparts; i++)
      fprintf(fp,"------------------------------");
    fprintf(fp,"-----------------------------------------------------------------------------------------------------------------------------------------------------------\n");
    fprintf(fp,"| %4s | %28s | %28s | %8s | %8s | %8s |","id","matching","method","CEW","TCC","TCC/CEW");
    
    for (i=0; i<nparts; i++){
      fprintf(fp,"   pro%2d VW   |",i);
    }
    for (i=0; i<nparts; i++){
      fprintf(fp,"   pro%2d ET   |",i);
    }
    fprintf(fp," %8s | %12s | %12s | %8s |\n","IF","MET","TET","ETIF");
    for (i=0; i<nparts; i++)
      fprintf(fp,"------------------------------");
    fprintf(fp,"-----------------------------------------------------------------------------------------------------------------------------------------------------------\n");
  }

  fprintf(fp,"| %4d | %28s | %28s | %8d | %8d | %8.3f |",
	  out_id, F_serialize, method, (int)v_cutedge, (int)v_tcc, v_tcc/v_cutedge);
  
  for (i=0; i<nparts; i++)
    fprintf(fp," %12d |", (int)v_proVW[i]);
  
  for (i=0; i<nparts; i++){
    fprintf(fp," %12.2lf |", v_proET[i]);
    if(v_proET[i] > et_max) et_max = v_proET[i];
    et_sum += v_proET[i];
  }

  fprintf(fp," %8.6lf | %12.2f | %12.2f | %8.6f |\n", 
	  v_imbalance, v_met, et_sum, nparts*et_max/et_sum);

  fclose(fp);
  
  /* .time.log */
  if(tool != TOOL_JOSTLE){
    strcpy(filename,F_output);
    strcat(filename, ".time.log");
    if ((fp=fopen(filename,"a"))==NULL){
      printf("WriteLog error code 5 : cannot generate output time log file %s\n",filename);
      exit(0);
    }
  
    if(ftell(fp) == 0){
      fprintf(fp,"# timing\n");
      fprintf(fp,"-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n");
      fprintf(fp,"| %4s | %28s | %28s | %18s | %18s | %18s | %18s | %18s |\n","id","matching","method","coarsening","initial part","uncoarsening","ET load balancing","total");
      fprintf(fp,"-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n");
    }
    fprintf(fp,"| %4d | %28s | %28s | %18.5lf | %18.5lf | %18.5lf | %18.5lf | %18.5lf |\n", out_id,F_serialize,method,t_coa,t_ini,t_ref,t_execbal,t_tot);
    fclose(fp);
  }

  U_free("WriteLog error code 5\n",&v_proVW,&v_proET,&method,LEND);
}

void IO_writeBetLog(int tool,void *graph, void *grid, int edgecut,
		    int ctype, int wtype, int rtype,
		    int cseed, int iseed, int rseed,
		    char* F_graph, char* F_grid, char* F_output,char *F_serialize,
		    int out_id){
  char filename[256];
  int i;
  FILE* fp;
  int nparts = 0;
  
  char *method = NULL;
  
  double v_cutedge,v_tcc,v_imbalance,v_met,v_met025,v_met05,v_met1,v_met2;
  int *v_proVW;
  double *v_proET;

  nparts = IO_stat(tool,graph,grid,&v_cutedge,&v_tcc,
		   &v_imbalance,&v_met,&v_met025,
		   &v_met05,&v_met1,&v_met2,
		   &v_proVW,&v_proET);


  CALL2VAL(tool,GetScheme(ctype,wtype,rtype,cseed,iseed,rseed),method);

  /* .vwt.log */
  strcpy(filename,F_output);
  strcat(filename, ".bet.log");
  if ((fp=fopen(filename,"a"))==NULL){
    printf("WriteLog error code 6 : cannot generate output time log file %s\n",filename);
    exit(0);
  }
  
  if (ftell(fp) == 0){
    fprintf(fp,"#%s\n",F_graph);
    fprintf(fp,"#%s\n",F_grid);    
    fprintf(fp,"# before exectime results (pagrid)\n"); 
    for (i=0; i<nparts; i++)
      fprintf(fp,"---------------");
    fprintf(fp,"------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n");
    fprintf(fp,"| %4s | %28s | %28s | %8s | %8s | %8s |","id","matching","Method","CEW","TCC","TCC/CEW");
    for (i=0; i<nparts; i++){
      fprintf(fp,"   pro%2d VW   |",i);
    }
    fprintf(fp," %8s | %12s | %12s | %12s | %12s |\n","IF","MET 0.25","MET 0.5","MET 1","MET 2");
    
    for (i=0; i<nparts; i++)
      fprintf(fp,"---------------");
    fprintf(fp,"------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n");
  }

  fprintf(fp,"| %4d | %28s | %28s | %8d | %8d | %8.3f |",  out_id,F_serialize, method, (int)v_cutedge, (int)v_tcc, v_tcc/v_cutedge);


  for (i=0; i<nparts; i++)
    fprintf(fp," %12d |", v_proVW[i]);

  fprintf(fp," %8.6f |", v_imbalance);
  fprintf(fp," %12.3f |", v_met025);
  fprintf(fp," %12.3f |", v_met05);
  fprintf(fp," %12.3f |", v_met1);
  fprintf(fp," %12.3f |\n", v_met2);	

  fclose(fp);

  U_free("WriteLog error code 5\n",&v_proVW,&v_proET,LEND);
}
/*  
ExecTime_FullWeight(GraphType* graph, GridType* grid){
  int i,j;
  double *exec = grid->exec;
  int nnpro = grid->npro;
  int istart,iend;
  int k,v,prov,w, prow;
  double *prospeed = grid->prospeed;
  int *adjidx = graph->adjidx;
  int *adjncy = graph->adjncy;
  int *adjwgt = graph->adjwgt;
  int *pro = graph->pro;
  int *vwgt = graph->vwgt;
  int nvtxs = graph->nvtxs;
  double **ratio = grid->ratio;
  int **connect = grid->connect;
  int **latency1 = grid->latency1;

  for(i = 0; i < nnpro; i++)
    exec[i] = 0;
  for(v = 0; v < nvtxs; v++){
    prov = pro[v];
    exec[prov] += vwgt[v]*prospeed[prov];
    istart = adjidx[v];
    iend = adjidx[v+1];
    for(i = istart; i < iend; i++){
      w = adjncy[i];
      prow = pro[w];
      if(prov != prow){
        exec[prov] += adjwgt[i] * ratio[prov][prow];
      }
    }
  }
  for(i = 0; i < nnpro; i++){
    for(j = 0; j < nnpro; j++){
      if(connect[i][j] > 0)
        exec[i] += latency1[i][j];
    }
  }
}
*/
